using System;
using System.Collections.Generic;
using System.Diagnostics;
using Mbs.Messaging.Imp;
using Mbs.Reflection;
using Mbs.ComponentModel.Container;

namespace Mbs.Messaging
{
    [Serializable]
    [Component(Id = "MessageBus", Service = typeof(IMessageBus))]
    public class MessageBus : MarshalByRefObject, IMessageBus//, IRemoteMessageBus
    {
        private readonly IMessageRepository Repository;
        public MessageBus() : this(new MessageRepository()) { }
        public MessageBus(IMessageRepository repository)
        {
            Debug.Assert(repository != null, "repository == null");

            this.Repository = repository;
        }

        #region Publish
        public void Publish(object sender, object msg)
        {
            if (msg != null)
            {
                Repository[msg.GetType()].Publish(sender, msg);
            }
        }

        public void Publish(string topic, object sender, object msg)
        {
            if (msg != null)
                Repository[topic, msg.GetType()].Publish(sender, msg);
        }

        public void Publish<TMessage>(TMessage message)
        {
            Repository.Subject<TMessage>().Publish(null, message);
        }

        public void Publish<TMessage>(string topic, TMessage message)
        {
            Repository.Subject<TMessage>(topic).Publish(null, Topic.Make<TMessage>(topic, message));
        }

        public void Publish<TMessage>(string topic, object sender, TMessage message)
        {
            Repository.Subject<TMessage>(topic).Publish(sender, Topic.Make<TMessage>(topic, message));
        }


        public void Publish<TMessage>(object sender, TMessage message)
        {
            Repository.Subject<TMessage>().Publish(sender, message);
        }
        #endregion

        public void RegisterListner(params ISimpleMessageListner[] hks)
        {
            Repository.ListnerManager.Register(hks);
        }
        public void UnRegisterListner(params ISimpleMessageListner[] hks)
        {
            Repository.ListnerManager.UnRegister(hks);
        }

        #region Subscribe

        public void Unsubscribe<TMessage>(ObserverHandler<TMessage> handler)
        {
            Repository.Subject<TMessage>().Subscriber.Unsubscribe(handler);
        }

        public void Unsubscribe<TMessage>(string topic, ObserverHandler<Topic<TMessage>> handler)
        {
            Repository.Subject<TMessage>(topic).Subscriber.Unsubscribe(handler);
        }

        public IDisposable SubscribePreFilter<TMessage>(PreFilterHandler<TMessage> handler)
        {
            return Repository.Subject<TMessage>().Subscriber.SubscribePreFilter(handler);
        }

        public IDisposable SubscribePreFilter<TMessage>(string topic, PreFilterHandler<Topic<TMessage>> handler)
        {
            return Repository.Subject<TMessage>(topic).Subscriber.SubscribePreFilter(handler);
        }

        public void UnsubscribePreFilter<TMessage>(PreFilterHandler<TMessage> handler)
        {
            Repository.Subject<TMessage>().Subscriber.UnsubscribePreFilter(handler);
        }

        public void UnsubscribePreFilter<TMessage>(string topic, PreFilterHandler<Topic<TMessage>> handler)
        {
            Repository.Subject<TMessage>(topic).Subscriber.UnsubscribePreFilter(handler);
        }

        public IDisposable SubscribeFilter<TMessage>(FilterHandler<TMessage> handler)
        {
            return Repository.Subject<TMessage>().Subscriber.SubscribeFilter(handler);
        }

        public IDisposable SubscribeFilter<TMessage>(string topic, FilterHandler<Topic<TMessage>> handler)
        {
            return Repository.Subject<TMessage>(topic).Subscriber.SubscribeFilter(handler);
        }

        public void UnsubscribeFilter<TMessage>(FilterHandler<TMessage> handler)
        {
            Repository.Subject<TMessage>().Subscriber.UnsubscribeFilter(handler);
        }

        public void UnsubscribeFilter<TMessage>(string topic, FilterHandler<Topic<TMessage>> handler)
        {
            Repository.Subject<TMessage>(topic).Subscriber.UnsubscribeFilter(handler);
        }

        public IDisposable Subscribe<TMessage>(ObserverHandler<TMessage> handler)
        {
            return Repository.Subject<TMessage>().Subscriber.Subscribe(handler);
        }


        public IDisposable Subscribe<TMessage>(string topic, ObserverHandler<Topic<TMessage>> handler)
        {
            return Repository.Subject<TMessage>(topic).Subscriber.Subscribe(handler);
        }

        #endregion

        internal IDisposable InternalSubscribe(params object[] handlers)
        {
            if (handlers == null || handlers.Length == 0)
                throw new ArgumentNullException("handlers");

            int count = handlers.Length;
            List<Key> keys = new List<Key>(count);
            for (int i = 0; i < count; i++)
            {
                var handlerType = handlers[i].GetType();
                if (handlerType.IsGenericType
                    && handlerType.GetGenericTypeDefinition() == MessageBusExtension._.ObserverHandlerType
                    && handlerType.GetGenericArguments().Length == 1)
                {
                    var dataType = handlerType.GetGenericArguments()[0];
                    keys.Add(Key.Make(dataType));
                }
                else
                    throw new ArgumentException("Invalid args");
            }

            var rs = new CompositeDisposableObject(count);
            for (int i = 0; i < count; i++)
            {
                var type = Type.GetTypeFromHandle(keys[i].Handle);
                var subscriber = Repository[type].GetProperty("Subscriber");
                var m = subscriber.GetType().GetMethod("Subscribe", new Type[] { typeof(ObserverHandler<>).MakeGenericType(type) });
                var unsubscriber = (IDisposable)m.Invoke(subscriber, new object[] { handlers[i] });
                rs.Add(unsubscriber);
            }

            return rs;
        }

        internal IDisposable InternalSubscribe(string[] topics, params object[] handlers)
        {
            if (topics == null || topics.Length == 0)
                throw new ArgumentNullException("topics");
            if (handlers == null || handlers.Length == 0)
                throw new ArgumentNullException("handlers");
            if (topics.Length != handlers.Length)
                throw new Exception("topics.Length != handlers.Length");

            int count = topics.Length;
            List<Key> keys = new List<Key>(count);
            for (int i = 0; i < count; i++)
            {
                var type = handlers[i].GetType();
                if (type.IsGenericType
                    && type.GetGenericTypeDefinition() == MessageBusExtension._.ObserverHandlerType
                    && type.GetGenericArguments().Length == 1)
                {
                    var topicType = type.GetGenericArguments()[0];
                    if (topicType.IsGenericType && topicType.GetGenericTypeDefinition() == MessageBusExtension._.TopicType)
                        keys.Add(Key.Make(topicType, topics[i]));
                    else
                        throw new ArgumentException("Invalid args");
                }
                else
                    throw new ArgumentException("Invalid args");
            }

            var rs = new CompositeDisposableObject(count);
            for (int i = 0; i < count; i++)
            {
                var type = Type.GetTypeFromHandle(keys[i].Handle);
                var subscriber = Repository[topics[i], type].GetProperty("Subscriber");
                var m = subscriber.GetType().GetMethod("Subscribe", new Type[] { typeof(ObserverHandler<>).MakeGenericType(type) });
                var unsubscriber = (IDisposable)m.Invoke(subscriber, new object[] { handlers[i] });
                rs.Add(unsubscriber);
            }

            return rs;
        }

        public void Remove<TMessage>()
        {
            Repository.Remove<TMessage>();
        }

        public void Remove<TMessage>(string topic)
        {
            Repository.Remove<TMessage>(topic);
        }
        public void Remove(Type type)
        {
            Repository.Remove(type);
        }

        public void Remove(string topic, Type type)
        {
            Repository.Remove(topic, type);
        }

        public override object InitializeLifetimeService()
        {
            return null;
        }

        public void Dispose()
        {
            if (Repository != null)
                Repository.Dispose();
        }

        //#region IRemoteMessageBus Members

        
        //IDisposable IRemoteMessageBus.Subscribe<TMessage>(IObserverHandler<TMessage> handler)
        //{
        //    return Subscribe<TMessage>(handler.Invoke);
        //}

        //IDisposable IRemoteMessageBus.Subscribe<TMessage>(string topic, IObserverHandler<Topic<TMessage>> handler)
        //{
        //    return Subscribe<TMessage>(topic, handler.Invoke);
        //}

        //void IRemoteMessageBus.Unsubscribe<TMessage>(IObserverHandler<TMessage> handler)
        //{
        //    Unsubscribe<TMessage>(handler.Invoke);
        //}

        //void IRemoteMessageBus.Unsubscribe<TMessage>(string topic, IObserverHandler<Topic<TMessage>> handler)
        //{
        //    Unsubscribe<TMessage>(topic, handler.Invoke);
        //}

        //IDisposable IRemoteMessageBus.SubscribePreFilter<TMessage>(IPreFilterHandler<TMessage> handler)
        //{
        //    return SubscribePreFilter<TMessage>(handler.Invoke);
        //}

        //IDisposable IRemoteMessageBus.SubscribePreFilter<TMessage>(string topic, IPreFilterHandler<Topic<TMessage>> handler)
        //{
        //    return SubscribePreFilter<TMessage>(topic, handler.Invoke);
        //}

        //void IRemoteMessageBus.UnsubscribePreFilter<TMessage>(IPreFilterHandler<TMessage> handler)
        //{
        //    UnsubscribePreFilter<TMessage>(handler.Invoke);
        //}

        //void IRemoteMessageBus.UnsubscribePreFilter<TMessage>(string topic, IPreFilterHandler<Topic<TMessage>> handler)
        //{
        //    UnsubscribePreFilter<TMessage>(topic, handler.Invoke);
        //}

        //IDisposable IRemoteMessageBus.SubscribeFilter<TMessage>(IFilterHandler<TMessage> handler)
        //{
        //    return SubscribeFilter<TMessage>(handler.Invoke);
        //}

        //IDisposable IRemoteMessageBus.SubscribeFilter<TMessage>(string topic, IFilterHandler<Topic<TMessage>> handler)
        //{
        //    return SubscribeFilter<TMessage>(topic,handler.Invoke);
        //}

        //void IRemoteMessageBus.UnsubscribeFilter<TMessage>(IFilterHandler<TMessage> handler)
        //{
        //    UnsubscribeFilter<TMessage>(handler.Invoke);
        //}

        //void IRemoteMessageBus.UnsubscribeFilter<TMessage>(string topic, IFilterHandler<Topic<TMessage>> handler)
        //{
        //    UnsubscribeFilter<TMessage>(topic, handler.Invoke);
        //}
        //#endregion
    }
}
